/*
 *
 * Date: 2017年11月15日
 * Time: 下午2:15:55
 *
 */

package com.lmx.local.business;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;

import com.lmx.common.util.CommonUtil;
import com.lmx.common.util.PropertiesFileUtil;
import com.lmx.local.SocketClient;
import com.lmx.persistence.service.local.LocalCameraService;
import com.lmx.repoDomain.local.tmodel.Camera;
import com.lmx.repoDomain.tmodel.LsMessage;

import net.bramp.ffmpeg.FFmpegExecutor;
import net.bramp.ffmpeg.FFmpegUtils;
import net.bramp.ffmpeg.FFprobe;
import net.bramp.ffmpeg.builder.FFmpegBuilder;
import net.bramp.ffmpeg.job.FFmpegJob;
import net.bramp.ffmpeg.probe.FFmpegProbeResult;
import net.bramp.ffmpeg.progress.Progress;
import net.bramp.ffmpeg.progress.ProgressListener;

/**
 * 视频处理服务
 * 
 * @author 梁慕学
 * @version 1.00
 */
@Service
@Scope("singleton")
public class VideoProcessor {
    private static final Logger log = LoggerFactory.getLogger(VideoProcessor.class);
    
    private Properties properties = PropertiesFileUtil.getApplicationProperties();
                
    @Autowired
    protected LocalCameraService cameraService;
    
    @Autowired
    protected SocketClient client;
    
    private FFmpegExtender ffmpeg;
    private List<Camera> cameraList = new ArrayList<Camera>();//在线设备列表
    /*
     * 初始化入口，从数据库中载入摄像头列表
     */
    public void init(){
        //使用ffmpeg cli项目，进行fmpeg的流处理
        //进行ffmpeg全局对象的初始化
        String ffmpegCommandPath = properties.getProperty("local.camera.ffmpegCommandPath");
        //定义监听者
        CustomRunProcessFunc func = new CustomRunProcessFunc();
        //把监听者绑定到ffmpeg对象
        try {
            this.ffmpeg = new FFmpegExtender(ffmpegCommandPath,func);
        } catch (IOException e) {
            log.error("ffmpeg init fail",e);
        }
    }
    
    /*
     * 启动远程推拉流
     */
    public void startRemoteLive(String businessId,String code) throws IOException{
        if(this.ffmpeg==null){
            this.init();
        }
    	int resFlag=0;
        Camera camera = this.cameraService.findByCode(code);
        if(camera==null){
            log.warn("no this camera");
            LsMessage msg = new LsMessage();
            msg.setCode("1001");
            msg.setBusinessId(businessId);
            msg.setDeviceCode(code);
            msg.setCreateTime(CommonUtil.getCurrTimes());
            msg.setSuccess(0);//推流失败
            client.sendMessage(msg);
            return;
        }
        String command = this.createStreamCommand(camera);
        log.debug("command:" + command);
        String ffprobeCommandPath = properties.getProperty("local.camera.ffprobeCommandPath");
        FFprobe ffprobe = new FFprobe(ffprobeCommandPath);
        FFmpegExecutor executor = new FFmpegExecutor(this.ffmpeg,ffprobe);
        //摄像头rtsp地址
        String cameraAddress = "rtsp://" + camera.getAccessName() + ":" + camera.getAccessPass()
            + "@" + camera.getIp() + ":" + camera.getPort().toString() + "/h264/ch1/main/av_stream";
        cameraAddress = "D:/temp/video/test.mp4";
        String remoteUrl = properties.getProperty("local.camera.remoteUrl");
        //输出流，远程视频服务地址
        String rtmpLink = remoteUrl + "/" + camera.getCode();
        log.debug("remoteUrl:" + remoteUrl);
        log.debug("cameraAddress:" + cameraAddress);
        FFmpegProbeResult in = ffprobe.probe(cameraAddress);
        log.debug("rtmpLink:" + rtmpLink);
        FFmpegBuilder builder = new FFmpegBuilder().addExtraArgs("-an")
            .setInput(in) 
            .addOutput(rtmpLink).setFormat("flv").setAudioCodec("aac").setVideoCodec("libx264")
            .setVideoResolution(640, 480)
            .done();
        //创建监听器
        CustomRunProcessFunc cpf = (CustomRunProcessFunc)this.ffmpeg.getFunc();
        LiveStreamListener listener = new LiveStreamListener(camera.getIp(),camera.getCode());
        cpf.add(listener);
        //进行推拉流
        FFmpegJob job = executor.createJob(builder, new ProgressListener() {
            // Using the FFmpegProbeResult determine the duration of the input
            final double duration_ns = in.getFormat().duration * TimeUnit.SECONDS.toNanos(1);
            int resFlag=0;
            @Override
            public void progress(Progress progress) {
            	String progressStatus = progress.status.toString();
                log.debug("progress.status:" + progress.status);
				if ("continue".equals(progressStatus) && resFlag == 0) {
				    //启动云端流监听
				    LiveStreamListener listener = VideoProcessor.this.ffmpeg.getListenerFromCode(camera.getCode());
				    log.debug("listener is:" + listener);
				    if(listener!=null){
				        log.debug("start live check");
				        listener.startRemoteLiveCheck();
				    }else{
				        log.warn("no listener to check:" + camera.getCode());
				    }
					resFlag = 1;
	                log.debug("session.sendMessage:" + CommonUtil.getCurrTime());
		            LsMessage msg = new LsMessage();
		            msg.setCode("1001");
		            msg.setBusinessId(businessId);
		            msg.setDeviceCode(code);
		            msg.setCreateTime(CommonUtil.getCurrTimes());
		            msg.setSuccess(1);
		            //发送推流成功指令，此时不一定能拉流
		            client.sendMessage(msg);
				}
                double percentage = progress.out_time_ns / duration_ns;
                log.debug(String.format(
                    "[%.0f%%] status:%s frame:%d time:%s ms fps:%.0f speed:%.2fx",
                    percentage * 100,
                    progress.status,
                    progress.frame,
                    FFmpegUtils.toTimecode(progress.out_time_ns, TimeUnit.NANOSECONDS),
                    progress.fps.doubleValue(),
                    progress.speed
                ));
            }
        });
        job.run();
    }
    
    /*
     * 停止推流
     * @param String code 摄像头序列号
     */
    public void stopRemoteLive(String code){
        CustomRunProcessFunc cpf = (CustomRunProcessFunc)this.ffmpeg.getFunc();
        cpf.getListenerFromCode(code).getProcess().destroy();
    }
    
    /*
     * 生成推拉流指令字符串
     */
    public String createStreamCommand(Camera camera){
        String remoteUrl = properties.getProperty("local.camera.remoteUrl");
        //摄像头rtsp地址
        String cameraAddress = "rtsp://" + camera.getAccessName() + ":" + camera.getAccessPass()
            + "@" + camera.getIp() + ":" + camera.getPort().toString() + "/h264/ch1/main/av_stream";
        //远程流媒体服务地址
        String rtmpLink = remoteUrl + "/" + camera.getCode();
        String command = "ffmpeg -i";
        String options = "-f flv -r -s -an";
        command = command + " \"" + cameraAddress + "\" " + options 
                + " \"" + rtmpLink + "\"";
        return command;
    }
}
